"""
支付相关验证器
提供支付创建、验证时的安全检查功能
"""
import hashlib
import time
from decimal import Decimal, ROUND_HALF_UP
from datetime import datetime, timedelta
from flask import current_app
from app import db

class PaymentValidator:
    """支付数据验证器"""
    
    # 支持的支付方式
    VALID_PAYMENT_METHODS = ['wechat', 'alipay']
    
    # 支付金额限制
    MIN_PAYMENT_AMOUNT = Decimal('0.01')  # 最小支付金额1分
    MAX_PAYMENT_AMOUNT = Decimal('99999.99')  # 最大支付金额
    
    # 支付频率限制
    PAYMENT_LIMIT_PER_USER_HOUR = 50  # 每用户每小时最多支付50次
    
    @classmethod
    def validate_payment_data(cls, data):
        """验证支付请求数据"""
        if not data or not isinstance(data, dict):
            raise ValueError("支付数据不能为空")
        
        # 验证必要字段
        required_fields = ['order_id']
        for field in required_fields:
            if field not in data:
                raise ValueError(f"缺少必要参数: {field}")
        
        order_id = data['order_id']
        payment_method = data.get('payment_method', 'wechat')
        
        # 验证订单ID
        cls._validate_order_id(order_id)
        
        # 验证支付方式
        cls._validate_payment_method(payment_method)
        
        return True
    
    @classmethod
    def _validate_order_id(cls, order_id):
        """验证订单ID"""
        if not order_id:
            raise ValueError("订单ID不能为空")
        
        if not isinstance(order_id, int) or order_id <= 0:
            raise ValueError("订单ID格式错误")
        
        return True
    
    @classmethod
    def _validate_payment_method(cls, payment_method):
        """验证支付方式"""
        if not payment_method:
            raise ValueError("支付方式不能为空")
        
        if payment_method not in cls.VALID_PAYMENT_METHODS:
            raise ValueError(f"不支持的支付方式: {payment_method}")
        
        return True
    
    @classmethod
    def verify_payment_amount(cls, order_id, amount):
        """验证支付金额是否与订单一致"""
        from app.models.order import Order
        
        order = Order.query.get(order_id)
        if not order:
            raise ValueError("订单不存在")
        
        # 将金额转换为Decimal进行精确比较
        order_amount = Decimal(str(order.amount)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        payment_amount = Decimal(str(amount)).quantize(Decimal('0.01'), rounding=ROUND_HALF_UP)
        
        if order_amount != payment_amount:
            current_app.logger.error(f"支付金额不匹配: 订单{order_id}, 订单金额{order_amount}, 支付金额{payment_amount}")
            raise ValueError("支付金额与订单金额不匹配")
        
        # 验证金额范围
        if payment_amount < cls.MIN_PAYMENT_AMOUNT:
            raise ValueError(f"支付金额不能少于{cls.MIN_PAYMENT_AMOUNT}元")
        
        if payment_amount > cls.MAX_PAYMENT_AMOUNT:
            raise ValueError(f"支付金额不能超过{cls.MAX_PAYMENT_AMOUNT}元")
        
        return True
    
    @classmethod
    def prevent_duplicate_payment(cls, order_id):
        """防止重复支付"""
        from app.models.payment import Payment
        
        # 检查是否已有未完成或已成功的支付
        existing_payment = Payment.query.filter(
            Payment.order_id == order_id,
            Payment.status.in_([0, 1])  # 0=处理中, 1=已成功
        ).first()
        
        if existing_payment:
            if existing_payment.status == 1:
                raise ValueError("订单已支付成功，无需重复支付")
            else:
                # 检查支付是否超时（30分钟）
                if existing_payment.created_at and \
                   datetime.now() - existing_payment.created_at < timedelta(minutes=30):
                    raise ValueError("订单支付处理中，请稍后再试")
                else:
                    # 支付已超时，可以创建新的支付
                    current_app.logger.info(f"支付超时，允许重新支付: payment_id={existing_payment.id}")
        
        return True
    
    @classmethod
    def check_payment_frequency(cls, user_id):
        """检查支付频率限制"""
        from app.models.payment import Payment
        
        # 检查用户在过去1小时内的支付次数
        one_hour_ago = datetime.now() - timedelta(hours=1)
        payment_count = Payment.query.filter(
            Payment.user_id == user_id,
            Payment.created_at >= one_hour_ago
        ).count()
        
        if payment_count >= cls.PAYMENT_LIMIT_PER_USER_HOUR:
            raise ValueError(f"支付过于频繁，每小时最多支付{cls.PAYMENT_LIMIT_PER_USER_HOUR}次")
        
        return True
    
    @classmethod
    def validate_order_status(cls, order):
        """验证订单状态是否可以支付"""
        if not order:
            raise ValueError("订单不存在")
        
        if order.status != 0:  # 0=未支付
            status_map = {1: '已支付', 2: '已取消', 3: '已完成', 4: '已退款'}
            status_text = status_map.get(order.status, '未知状态')
            raise ValueError(f"订单状态为{status_text}，无法支付")
        
        # 检查订单是否过期（创建超过24小时）
        if order.created_at and \
           datetime.now() - order.created_at > timedelta(hours=24):
            raise ValueError("订单已过期，无法支付")
        
        # 检查预约时间是否已过
        if order.reservation_date and order.start_time:
            reservation_datetime = datetime.combine(order.reservation_date, order.start_time)
            if reservation_datetime < datetime.now():
                raise ValueError("预约时间已过，无法支付")
        
        return True
    
    @classmethod
    def validate_user_permission(cls, order, user_id):
        """验证用户是否有权限支付该订单"""
        if not order:
            raise ValueError("订单不存在")
        
        if order.user_id != user_id:
            raise ValueError("无权操作该订单")
        
        return True
    
    @classmethod
    def generate_payment_signature(cls, payment_no, amount, timestamp=None):
        """生成支付签名"""
        if not timestamp:
            timestamp = int(time.time())
        
        # 获取密钥（应该从配置中获取）
        secret_key = current_app.config.get('PAYMENT_SECRET_KEY', 'default_secret')
        
        # 构建签名字符串
        sign_string = f"payment_no={payment_no}&amount={amount}&timestamp={timestamp}&key={secret_key}"
        
        # 生成MD5签名
        signature = hashlib.md5(sign_string.encode('utf-8')).hexdigest()
        
        return signature
    
    @classmethod
    def verify_payment_signature(cls, payment_no, amount, timestamp, signature):
        """验证支付签名"""
        expected_signature = cls.generate_payment_signature(payment_no, amount, timestamp)
        
        if signature != expected_signature:
            raise ValueError("支付签名验证失败")
        
        # 检查时间戳是否过期（5分钟内有效）
        current_timestamp = int(time.time())
        if abs(current_timestamp - int(timestamp)) > 300:
            raise ValueError("支付签名已过期")
        
        return True
    
    @classmethod
    def validate_payment_callback_data(cls, data):
        """验证支付回调数据"""
        if not data or not isinstance(data, dict):
            raise ValueError("回调数据格式错误")
        
        # 验证必要字段
        required_fields = ['out_trade_no', 'trade_status', 'total_amount']
        for field in required_fields:
            if field not in data:
                raise ValueError(f"回调数据缺少字段: {field}")
        
        # 验证交易状态
        trade_status = data.get('trade_status')
        valid_statuses = ['TRADE_SUCCESS', 'TRADE_FINISHED', 'TRADE_CLOSED']
        if trade_status not in valid_statuses:
            raise ValueError(f"无效的交易状态: {trade_status}")
        
        return True
    
    @classmethod
    def clean_payment_data(cls, data):
        """清理支付数据"""
        if not isinstance(data, dict):
            return data
        
        cleaned_data = {}
        for key, value in data.items():
            if key in ['order_id', 'user_id']:
                # 确保ID是整数
                try:
                    cleaned_data[key] = int(value)
                except (ValueError, TypeError):
                    raise ValueError(f"{key}必须是数字")
            elif key == 'amount':
                # 确保金额是有效的数字
                try:
                    cleaned_data[key] = float(value)
                except (ValueError, TypeError):
                    raise ValueError("支付金额格式错误")
            elif isinstance(value, str):
                # 清理字符串
                cleaned_data[key] = value.strip()
            else:
                cleaned_data[key] = value
        
        return cleaned_data
    
    @classmethod
    def log_payment_operation(cls, operation, payment_id=None, order_id=None, user_id=None, details=None):
        """记录支付操作日志"""
        log_data = {
            'operation': operation,
            'payment_id': payment_id,
            'order_id': order_id,
            'user_id': user_id,
            'timestamp': datetime.now(),
            'details': details or {}
        }
        
        current_app.logger.info(f"支付操作日志: {log_data}")
        
        # 这里可以将日志写入专门的支付日志表
        # 为了简化，先写入应用日志 